/*
 * @file	rtc.c
 * @autohr  Queclink beck.wan
 * @date	2017年3月21日
 * @brief	 
 *  
 * Coypright (C) 2017 Queclink Co., Ltd. All Rights Reserved
 *
 * @attention
 * 
 * @usage
 * 
 * @record   
 * 不使用GSM时间:
 * +CCLK: "00/01/01,01:14:15+32"
 * OK
 * [  PUB]: modem tm=0100-00-01 01:14:15,32!
 *
 */

//#include "cmd_tma.h"
//#include "work_tma.h"
//#include "work_dog.h"
#include <stdio.h>
#include "task_misc.h"
//#include "low_power.h"
//#include <hdl/rtc.h>
#include "rtc_app.h"
//#include <aadl/gps.h>
#include <time.h>
#include "bsp.h"
#include "elog.h"
#include "task_gnss.h"
#include "gnss_power_mgr.h"

/* rtc时间同步数据维护结构 */
static rtc_info_t rtc_info={
    .rtc.tm_year = 2024,
    .rtc.tm_mon  = 1,
    .rtc.tm_mday = 1,
    .rtc.tm_hour = 0,
    .rtc.tm_min  = 0,
    .rtc.tm_sec  = 0
};

/* RTC默认时间 */
struct tm rtc_default_time = {
    .tm_year = 2024,
    .tm_mon  = 1,
    .tm_mday = 1,
    .tm_hour = 0,
    .tm_min  = 0,
    .tm_sec  = 0
};


/**
 * RTC初始化
 */
void rtc_app_init(void)
{
    rtc_info.rum_tim = getms();
    rtc_info.gps_fix = 0;
}

#if 0

/**
 * 同步RTC时间
 * @param rtc
 * @return
 */
static int rtc_set_time(struct tm rtc)
{
    int result = 0;

    if ((rtc.tm_year < 1900) || (rtc.tm_mon < 1))
        return -EINVAL;

    rtc.tm_year  = rtc.tm_year - 1900;
    rtc.tm_mon   = rtc.tm_mon  - 1;
    rtc.tm_isdst = -1;
    result = hdl_rtc_set(&rtc);

    return result;
}

/**
 * 获取RTC时间
 * @param rtc
 * @return
 */
int rtc_get_time(struct tm *rtc)
{
    int i = 0;
    int result = 0;
    struct tm tim;

    for (i = 0; i < 5; i++) {
        result = hdl_rtc_get(rtc);
        if (0 != result)
            continue;
        result = hdl_rtc_get(&tim);
        if (0 == result)
          break;
    }
    if (i >= 5) {
        *rtc =  rtc_info.rtc;
        return 0;
    }

    if (rtc->tm_sec != tim.tm_sec) {
        rtc->tm_year = tim.tm_year + 1900;
        rtc->tm_mon  = tim.tm_mon + 1;
        rtc->tm_mday = tim.tm_mday;
        rtc->tm_hour = tim.tm_hour;
        rtc->tm_min  = tim.tm_min;
        rtc->tm_sec  = tim.tm_sec;
    } else {
        rtc->tm_year += 1900;
        rtc->tm_mon  += 1;
    }
    rtc_info.rtc = *rtc;

    return 0;
}
#endif


__weak int stdlib_days_of_mon(int month, int year)
{
    int mday;
    const int mday_fix[13] = {0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	if (month > 12)
		return 0;
	if (month != 2)
		return mday_fix[month];	
    
    if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0) )
        mday=29;
    else
        mday=28;
	
    return mday;
}

/*******************************************************************************
* @brief	   计算星期(基姆拉尔森算法)
* @param[in]   y m d - 年 月 日
* @return 	   星期(0 Sun, 1 Mon, 2 Tue, 3 Wed, 4 Thu, 5 Fri, 6 Sat)
*******************************************************************************/
__weak int stdlib_day_of_week(int y, int m, int d)  
{  
	if( m==1 || m==2 )
	{
		m += 12;
		y--;		
	}
    return (d + 1 + 2*m + 3*(m+1)/5 + y +y/4 - y/100 + y/400 )%7;
} 


/**
 * 检查时间合法
 * @param rtc
 * @return
 */
int rtc_check_is_valid(struct tm time)
{
    /* 检查年 */
    if (time.tm_year < 0)
        return -1;
    /* 检查月 0--12 */
    if ((time.tm_mon < 0) || (time.tm_mon > 12))
        return -1;
    /* 检查日 1--31 */
    if ((time.tm_mday < 1) || (time.tm_mday > stdlib_days_of_mon(time.tm_mon, time.tm_year)))
        return -1;
    /* 检查时 0--23 */
    if ((time.tm_hour < 0) || (time.tm_hour > 23))
        return -1;
    /* 检查分 0--59 */
    if ((time.tm_min < 0) || (time.tm_min > 59))
        return -1;
    /* 检查秒 0--59 */
    if ((time.tm_sec < 0) || (time.tm_sec > 59))
        return -1;

    return 0;
}


/**
 * 将时间转化成秒
 * @param time: 当前时间
 * @return
 */
time_t time_to_timestamp(const struct tm *ptime)
{
    struct tm time;
    memcpy(&time, ptime, sizeof(struct tm));
    time.tm_year = time.tm_year - 1900;
    time.tm_mon = time.tm_mon - 1;
    time.tm_isdst = -1;
    return mktime(&time);
}

/**
 * 将时间转化成秒
 * @param time: 当前时间
 * @return
 */
time_t tma_time_to_second(struct tm time)
{
    time.tm_year  = time.tm_year - 1900;
    time.tm_mon   = time.tm_mon  - 1;
    time.tm_isdst = -1;

    return mktime(&time);
}


/**
 * 将秒转化成时间
 * @param second
 * @param rtc
 */
void tma_second_to_time(time_t second, struct tm *rtc)
{
    *rtc = *localtime((const time_t *) &second);
    rtc->tm_year += 1900;
    rtc->tm_mon  += 1;
}


tma_config_t *get_tma_config(void);
time_t tma_seconds(tma_config_t *tma_config);


/**
 * 获取UTC时间戳(报文UTC时间戳使用，不带时区)
   带时区的使用get_rtc_timestamp接口
 */
unsigned int get_timestamp(void)
{
    static time_t timestamp = 0;                       
    struct tm t;
    if (!rtc_get_time(&t)){
        timestamp = time_to_timestamp(&t); 
        
        tma_config_t *cfg = get_tma_config();
        timestamp -= tma_seconds(cfg);
    }
    return timestamp;
}

/**
 * 获取RTC时间戳(本地时间戳使用，带时区)
 */

unsigned int get_rtc_timestamp(void)
{
    static time_t timestamp = 0;                       
    struct tm t;
    if (!rtc_get_time(&t)){
        timestamp = time_to_timestamp(&t);
    }
    return timestamp;
}



#if 0
unsigned int get_timestamp_test(void)
{
    static time_t timestamp = 0;                        //当前时间戳
    struct tm t;
    log_i("get_timestamp_test exec.", timestamp);
    if (!rtc_get_time(&t)){
          log_i("get_timestamp_test: rtc get tm=%04d-%02d-%02d %02d:%02d:%02d!\r\n",
            t.tm_year, t.tm_mon, t.tm_mday,
            t.tm_hour, t.tm_min, t.tm_sec);
        timestamp = time_to_timestamp(&t);

        tma_config_t *cfg = get_tma_config();
        timestamp -= tma_seconds(cfg);
        log_i("get_timestamp_test: timestamp: [0x%x] or [%u]", timestamp, timestamp);
    } else {
        log_i("get_rtc time failed.");
    }
    
    return timestamp;
}
#endif


struct tm debug_rtc;

void debug_set_rtc_time(void)
{
    log_i("debug rtc set tm=%04d-%02d-%02d %02d:%02d:%02d!\r\n",
            debug_rtc.tm_year, debug_rtc.tm_mon, debug_rtc.tm_mday,
            debug_rtc.tm_hour, debug_rtc.tm_min, debug_rtc.tm_sec);
    debug_rtc.tm_sec = 0;
}


extern bool get_gps_rtc_accurate_check(void);
extern void set_gps_rtc_accurate_check(bool val);


void quec_util_dog_time_adjust_last(stdlib_time_struct *new_rtc_time)
{
#if 0
    stdlib_time_struct cur;
    unsigned int tdiff = 0;
	dog_config_t *dog_config = get_dog_config();
	
    if (QUEC_DOG_INTERVAL_ENABE == dog_config->mode) {
        applib_dt_get_rtc_time(&cur);

        if(stdlib_get_time_diff(new_rtc_time,&cur, &tdiff)) {
            stdlib_adjust_time(&dog_start_time,tdiff,&dog_start_time,0);
        } else {
            stdlib_get_time_diff(&cur,new_rtc_time,&tdiff);
            stdlib_adjust_time(&dog_start_time,tdiff,&dog_start_time,1);
        }            
    }
#else
#warning "Need watchdog reset counter adjust impl by rtc time modification."
#endif
}

/**
 * 同步其他功能时间
 * @param new_tm:新时间
 * @param old_tm:旧时间
 */
void tma_set_rtc_hook(struct tm new_tm, struct tm old_tm)
{
#if 0
    /* 校准ACC点火/熄火时间 */
    ign_igf_adjust_ignition_time(new_tm, old_tm);

    /* 校准ACC点火工时 */
    hmc_adjust_ignition_time(new_tm, old_tm);

    /* 校准低功耗工作时长 */
    //low_power_adjust_time(new_tm, old_tm);

    /* IDL功能的时间校准 */
    idl_adjust_time(new_tm, old_tm);
#else
#warning "Need misc counter adjust impl by rtc time modification."
#endif
}


#warning "Need GTTMA impl in rtc time modification, TMA is +8 Zone now."

/* 默认GTTMA参数 东8区，后续需要调整*/
static tma_config_t tma_default_config = {
    .sign = '+',
	.hour_offset = 8,
    
};


tma_config_t *get_tma_config(void)
{
    return &tma_default_config;
}



time_t tma_seconds(tma_config_t *tma_config)
{
    time_t tm_second = 0;
    /* 开启夏令时，时间加3600S */
    if (tma_config->daylight_saving)
        tm_second += HOURS_PER_SECOND;

    /* UTC时间转化为本地时间 */
    if ('+' == tma_config->sign) {
        tm_second += (tma_config->hour_offset * HOURS_PER_SECOND);
        tm_second += (tma_config->minute_offset * MINUTE_PER_SECOND);
    } else {
        tm_second -= (tma_config->hour_offset * HOURS_PER_SECOND);
        tm_second -= (tma_config->minute_offset * MINUTE_PER_SECOND);
    }

    return tm_second;
}


/**
 * tma同步RTC时间
 * @param tma_config
 * @param old_tm
 * @param tm_second
 * @return
 */
int tma_set_rtc(tma_config_t tma_config, time_t tm_second)
{
    int result = 0;
    struct tm new_tm;

   tm_second += tma_seconds(&tma_config);
    
    tma_second_to_time(tm_second, &new_tm);
    result = rtc_sync_time(new_tm);

    return result;
}



#include "bsp/inc/bsp_rtc.h"
/**
 * 同步时间
 * @param sync_rtc
 * @return 返回0,其他失败
 */
int rtc_sync_time(const struct tm new_tm)
{
    int result = -1;
    time_t set_second = 0;
    time_t get_second = 0;
    struct tm old_tm;
	stdlib_time_struct new_time;

    result = rtc_check_is_valid(new_tm);
    if (0 != result)
        return -EINVAL;

    rtc_get_time(&old_tm);
    get_second = tma_time_to_second(old_tm);
    set_second = tma_time_to_second(new_tm);
    //if ( (abs(get_second - set_second) > 1) || get_gps_rtc_accurate_check()) {
    if ( (get_time_sync_flg() == 0) || (abs(get_second - set_second) > 2) || (get_second > set_second) ) {
    //if (get_second != set_second) {
        new_time.year    = new_tm.tm_year;
        new_time.month   = new_tm.tm_mon;
        new_time.day     = new_tm.tm_mday;
        new_time.hour    = new_tm.tm_hour;
        new_time.min     = new_tm.tm_min;
        new_time.sec     = new_tm.tm_sec;
        new_time.weekday = new_tm.tm_wday;
        quec_util_dog_time_adjust_last(&new_time);//added by cheney
        result = bsp_rtc_set((void *)&new_tm);
        /* 同步其他时间参数 */
        if (0 == result)
            tma_set_rtc_hook(new_tm, old_tm);

        //set_gps_rtc_accurate_check(false);

        debug_rtc = new_tm;
        log_i("rtc set result=%d,old=%04d-%02d-%02d %02d:%02d:%02d,"
             "new=%04d-%02d-%02d %02d:%02d:%02d!\r\n", result,
             old_tm.tm_year, old_tm.tm_mon, old_tm.tm_mday,
             old_tm.tm_hour, old_tm.tm_min, old_tm.tm_sec,
             new_tm.tm_year, new_tm.tm_mon, new_tm.tm_mday,
             new_tm.tm_hour, new_tm.tm_min, new_tm.tm_sec);
    } else {
        result  = 1;
    }

    return result;
}

static int cmp_time(struct tm *t1,  struct tm *t2)
{
    if (t1->tm_year > t2->tm_year) {
        return 1;
    } else if (t1->tm_year < t2->tm_year) {
        return 2;
    }

    if (t1->tm_mon > t2->tm_mon) {
        return 1;
    } else if (t1->tm_mon < t2->tm_mon) {
        return 2;
    }

    if (t1->tm_mday > t2->tm_mday) {
        return 1;
    } else if (t1->tm_mday < t2->tm_mday) {
        return 2;
    }

    if (t1->tm_hour > t2->tm_hour) {
        return 1;
    } else if (t1->tm_hour < t2->tm_hour) {
        return 2;
    }

    if (t1->tm_min > t2->tm_min) {
        return 1;
    } else if (t1->tm_min < t2->tm_min) {
        return 2;
    }

    if (t1->tm_sec > t2->tm_sec) {
        return 1;
    } else if (t1->tm_sec < t2->tm_sec) {
        return 2;
    }

    return 0;    
}

static struct tm * chk_two_time( struct tm *t1,  struct tm *t2)
{
    bool t1_valid = false;
    bool t2_valid = false;

    struct tm *t = NULL;

     if (rtc_check_is_valid(*t1) == 0)
           t1_valid = true;

     if (rtc_check_is_valid(*t2) == 0)
           t2_valid = true;

    if (t1_valid && t2_valid) {
        int c = cmp_time(t1,  t2);
        if (c == 1)
            t = t1;
        else
            t = t2;
    } else if (t1_valid) {
        t = t1;
    } else if  (t2_valid) {
        t = t2;
    }

    return t;
}
/**
 * 设备上电更新RTC时间
 */
int rtc_update_from_flash(void)
{
    int result = 0;
    time_t second = 0;
    struct tm rtc_time = { 0 };
    tma_config_t cfg;
    struct tm *t = NULL;
     
    rtc_get_time(&rtc_time);
    t = &rtc_time;
        
    if (MISC_WRITE_FLASH_END == misc_save.save_flag && RTC_SAVE_FLAG == misc_save.rtc_flag) {
        t = chk_two_time(&rtc_time, &misc_save.rtc);
    }

    if (t != NULL) {
        t = chk_two_time(t, &rtc_default_time);
    }

 
    if (t == &rtc_default_time || t == NULL) {
        cfg = *get_tma_config();
        second = tma_time_to_second(rtc_default_time);
        result = tma_set_rtc(cfg, second);
    } else {
        if (t != &rtc_time)
            result = rtc_sync_time(*t);
    }

    return result;
}

unsigned char gps_sync_flag = 0;
unsigned long last_gps_sync_time = 0;
bool modem_sync_flag = false;
unsigned int last_modem_sync_time = 0;
unsigned char get_time_sync_flg(void)
{
    unsigned char flg = 0;

    if (gps_sync_flag && !timeout_sec(last_gps_sync_time, 24*3600))
        flg |= 1;
    if (modem_sync_flag && !timeout_sec(last_modem_sync_time, 24*3600))
        flg |= 2;

    return flg;

}

/**
 * gps同步rtc时间
 */
void rtc_update_from_gps(void)
{
    int result = 0;
    time_t second = 0;
    //struct tm old_tm;
    tma_config_t cfg;
    struct fix_info gps;

    if (gps_sync_flag == 1) {
        if (timeout_sec(last_gps_sync_time, 24*3600))  //24小时内无定位，把校时标志清除
            gps_sync_flag = 0;
    }
	
    cfg = *get_tma_config();
	
    get_last_gnss_info(&gps);
    if (FIX_OK != gps.state) {
        rtc_info.gps_fix = RTC_GPS_NO_FIX;
        return;
    }

    //rtc_get_time(&old_tm);
    gps.utc.tm_year += 1900;
    gps.utc.tm_mon  += 1;
    second = tma_time_to_second(gps.utc);
    result = tma_set_rtc(cfg, second);
    if (result < 0) {
        log_e("rtc gps utc to rtc error result=%d!\r\n", result);
    } else {
        gps_sync_flag = 1;
        last_gps_sync_time = getsec();
    }    
    /* 从之前gps.state未定位状态到定位, 存储RTC时间 */
    if (RTC_GPS_NO_FIX == rtc_info.gps_fix) {
        rtc_info.gps_fix = 0;
        rtc_save_to_flash(1);
    }
}

/**
 * modem同步rtc时间
 */
void rtc_update_from_modem(struct tm *new_time, int time_zone)
{
    int result = 0;
    time_t second = 0;
    tma_config_t *tma_cfg = NULL;

    /* 上电后必须从Modem同步一次获取当地时区，否则判断为未完成同步 */
    if ((get_time_sync_flg() & 2) != 0)
        return;

    second = mktime(new_time);

    time_zone /= 4;                                 //time_zone单位是15分钟
    second += time_zone * 3600;

    tma_cfg = get_tma_config();                     //更新配置
    tma_cfg->hour_offset = abs(time_zone);
    tma_second_to_time(second, new_time);
    result = rtc_sync_time(*new_time);

    if (result < 0) {
        log_e("update rtc from modem result=%d!\r\n", result);
    } else {
        modem_sync_flag = true;
        last_modem_sync_time = getsec();
        log_i("update rtc from modem finished. TimeZone:%c%d\r\n", time_zone >= 0 ? '+' : '-', time_zone);

        rtc_save_to_flash(1);
    }
}

/**
 * 每隔5分钟存储RTC时间
 * @param type:1:立刻存储,其他每隔5min存储一次
 */
void rtc_save_to_flash(unsigned char type)
{
    int result = 0;
    struct tm rtc;

    if (1 != type) {
        if (!timeout_sec(rtc_info.save_tim, RTC_SAVE_TIM))
            return;
    }
    
    rtc_info.save_tim = getsec();
    
    result = rtc_get_time(&rtc);
    if (0 != result)
        return;

    misc_save.rtc_flag = RTC_SAVE_FLAG;
    misc_save.rtc      = rtc;

    misc_write_data_to_flash();
}

/**
 * 每秒同步一次时间
 */
void rtc_update_handler(void)
{
    //if (!is_timeout(rtc_info.rum_tim, RTC_UPDATE_TIM))
    //    return;
    rtc_info.rum_tim = getms();
    /* 更新RTC时间 */
    rtc_update_from_gps();

    /* 每隔5分钟存储RTC时间 */
    rtc_save_to_flash(0);
}

void tm_to_stdlib_time(struct tm *tm_time, stdlib_time_struct *std_time)
{
	std_time->sec = tm_time->tm_sec;
	std_time->min = tm_time->tm_min;
	std_time->hour = tm_time->tm_hour;
	std_time->day = tm_time->tm_mday;
	std_time->month = tm_time->tm_mon;
	std_time->year = tm_time->tm_year;
	std_time->weekday= tm_time->tm_wday;
}
/**
 * 返回RTC时间
 * @param time
 * @return
 */
int applib_dt_get_rtc_time(stdlib_time_struct *time)
{
    struct tm t;
    int err = rtc_get_time(&t);

    if (0 == err) 
        tm_to_stdlib_time(&t, time);

    return err;
}


int timetostr(stdlib_time_struct *t, char *buf, int len) 
{
    return snprintf(buf, len, ",%04d%02d%02d%02d%02d%02d",
           t->year,t->month,t->day, t->hour, t->min, t->sec);    
 }

#if 0

/* RTC秒数(since 1970)*/
unsigned int get_rtc_sec(void)
{
    struct tm now;
    rtc_get_time(&now); 
    return mktime(&now);
}

bool is_rtc_sec_timeout(unsigned int start_time, unsigned int timeout)
{
    unsigned int curr_sec = get_rtc_sec();
    if ((curr_sec - start_time) >= timeout)
        return true;
    else
        return false;
}


int get_utc_time_from_rtc(struct tm *utc)
{
    struct tm tm;
    time_t tm_second;
    int retv = 0;
    
    rtc_get_time(&tm);   
    if (rtc_check_is_valid(tm) != 0)
        retv = -1;
        
    tm_second = tma_time_to_second(tm);
    tm_second -= tma_seconds(get_tma_config());
    *utc = *localtime(&tm_second);

    return retv;
}


int applib_utc_get_rtc_time(stdlib_time_struct *time)
{
    struct tm t;
    struct tm utc_time;
    time_t second = 0;    
    tma_config_t *tma_config;

    tma_config = get_tma_config();
    rtc_get_time(&t);
    /* 本地时间转UNIX时间戳(秒) */
    t.tm_year  = t.tm_year - 1900;
    t.tm_mon   = t.tm_mon  - 1;
    t.tm_isdst = 0; /* 不是夏令时*/
    second = mktime(&t);
    /* 本地时间转为UTC时间 */
    if ('+' == tma_config->sign) {
        second -= (tma_config->hour_offset * 3600);
        second -= (tma_config->minute_offset * 60);
    } else {
        second += (tma_config->hour_offset * 3600);
        second += (tma_config->minute_offset * 60);
    }
    /* 本地时间转为UTC时间 (开启夏令时，时间减3600S) */
    if (tma_config->daylight_saving) {
        second -= 3600;
    }
    utc_time = *localtime((const time_t *)&second);
    /* 转为完整形式 */
    time->year    = utc_time.tm_year + 1900;
    time->month   = utc_time.tm_mon + 1;
    time->day     = utc_time.tm_mday;
    time->hour    = utc_time.tm_hour;
    time->min     = utc_time.tm_min;
    time->sec     = utc_time.tm_sec;
    time->weekday = utc_time.tm_wday;

    return 0;
}

#endif




